home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / System / Sample 2.4 Think C distribution / rep.c < prev    next >
Text File  |  1990-07-11  |  31KB  |  990 lines

  1. /*______________________________________________________________________
  2.  
  3.     rep.c - Report Module.
  4.     
  5.     Copyright © 1988, 1989, 1990 Northwestern University.  Permission is granted
  6.     to use this code in your own projects, provided you give credit to both
  7.     John Norstad and Northwestern University in your about box or document.
  8.     
  9.     This reusable module implements scrolling reports.
  10.     
  11.     The caller supplies a rectangle in which the report should be
  12.     displayed.  When the report is created the caller is returned a handle
  13.     to a report record.  This handle must be passed to all subsequent
  14.     routines.
  15.     
  16.     There are two types of reports.
  17.     
  18.     In type 0 reports the report is intially empty, and the routines
  19.     rep_Append and rep_Fill are used to append one or more lines at a time.
  20.     The maximum report size is 32k characters of text.
  21.     
  22.     In type 1 reports the text is fixed, in consecutive purgable STR# 
  23.     resources specified when the report is initialized.  rep_Append and
  24.     rep_Fill do nothing.  The maximum report size is 8k lines of text,  
  25.     with no limit on characters.
  26.     
  27.     Type 0 reports are appropriate for reports generated dynamically by the
  28.     program.  For example, the output of a volume scanner.  Type 1 reports
  29.     are more appropriate for help windows and other kinds of fixed text.
  30.     
  31.     Type 1 reports use memory more efficiently than type 0 reports, and they
  32.     can handle much larger reports (8k lines vs. 32k chars).  With type 0
  33.     reports the entire report is stored in one big memory buffer, while with
  34.     type 1 reports the purgable STR# resources act as a read-only virtual 
  35.     memory.  Both types use unpurgeable memory, but type 0 requires
  36.     1 byte per character, while type 1 only requires 4 bytes per line.
  37.     
  38.     The module is currently implemented using the list manager, and the 
  39.     report handle is just a list handle.  But you shouldn't rely on 
  40.     this - the module could be rewritten to use TextEdit or some other kind
  41.     of code, without affecting its official interface.
  42.     
  43.     Multiple reports in different rectangles can be managed simultaneously.
  44.     
  45.     All of the code is placed in its own segment named "rep", except for the
  46.     routine rep_Init, which is placed in segment repi.
  47. _____________________________________________________________________*/
  48.  
  49. #include    <string.h>
  50.  
  51. #include "rep.h"
  52. #include "utl.h"
  53. #include "doc.h"
  54.  
  55. #define nil 0
  56.  
  57. /*______________________________________________________________________
  58.  
  59.     rep_Init - Initialize a Report.
  60.     
  61.     Entry:    repRect = ptr to rectangle enclosing report and its scroll bar.
  62.                 theWindow = ptr to window containing report.
  63.                 repType = report type (0 or 1).
  64.                 firstID = id of first STR# resource (type 1 only).
  65.                 lDefRezID = id of LDEF (type 1 only).
  66.                 cellRezID = id of CELL resource (type 1 only).
  67.     
  68.     Exit:        repHandle = handle to report record.
  69.     
  70.     For best results, the rectangle height should be 2 more than an even
  71.     multiple of the window font's ascent plus descent plus leading
  72.     (12 for Geneva 9). This will make an even number of rows appear in 
  73.     the rectangle, which makes scrolling look nice.
  74.     
  75.     listDefID must be the id of the special LDEF designed just for use with
  76.     type 1 reports.  You default value is 128, but you can change this if
  77.     it conflicts with your own LDEFs.
  78. _____________________________________________________________________*/
  79.  
  80.  
  81. void rep_Init (Rect *repRect, WindowPtr theWindow, 
  82.     short repType, short firstID, 
  83.     short lDefRezID, short cellRezID, Handle *repHandle)
  84.  
  85. {
  86.     Rect                rView;            /* list manager rectangle */
  87.     Rect                dataBounds;        /* dimensions of list */
  88.     Point                cSize;            /* cell size */
  89.     ListHandle        theList;            /* handle to list manager record */
  90.  
  91.     /* Variables used only for type 1 records. */    
  92.  
  93.     Handle            theStrings;            /* handle to STR# resource */
  94.     Rect                frameRect;            /* rectangle enclosing the list */
  95.     auxInfo            **aux;                /* handle to auxiliary info */
  96.     short                i;                        /* loop index */
  97.     short                count;                /* number of lines in STR# rsrc */
  98.     short                *pCellArray;        /* pointer into cell array */
  99.     short                cellIndex;            /* index in cell data */
  100.     
  101.     /* Set the list manager rectangle.  Inset the rectangle passed by the
  102.         caller by one pixel to accomodate a framing rectangle, and subtract
  103.         15 from the right side to accomodate the scroll bar. */
  104.     
  105.     rView = *repRect;
  106.     InsetRect(&rView, 1, 1);
  107.     rView.right -= 15;
  108.     
  109.     /* Create an empty list. Save the report type in the refCon field of
  110.         the list record. */
  111.     
  112.     SetPt(&cSize, 0, 0);
  113.     SetRect(&dataBounds, 0, 0, 1, 0);
  114.     theList = (ListHandle) LNew(&rView, &dataBounds, cSize, 
  115.         repType ? lDefRezID : 0, theWindow, 
  116.         false, false, false, true);
  117.     *repHandle = (Handle) theList;
  118.     (**theList).selFlags = lOnlyOne;
  119.     (**theList).refCon = repType;
  120.     
  121.     /* If type 0 return handle to empty list. */
  122.     
  123.     if (!repType) return;
  124.     
  125.     /* If type 1 report initialize the fixed cell data. */
  126.     
  127.     /* Dispose of the empty cell block created by the LNew call.  Plug in a
  128.         handle to the CELL resource precomputed by the cvrt tool.  Make the
  129.         resource unpurgable. */
  130.         
  131.     DisposHandle((Handle)(**theList).cells);
  132.     (**theList).cells = (DataHandle)GetResource((ResType)'CELL', cellRezID);
  133.     HNoPurge((Handle)(**theList).cells);
  134.     
  135.     /* Add the proper number of rows to the list. */
  136.     
  137.     count = GetHandleSize((Handle)(**theList).cells) / 3;
  138.     (void) LAddRow(count, 0, theList);
  139.     
  140.     /* Initialize the cell array. */
  141.     
  142.     pCellArray = (**theList).cellArray;
  143.     cellIndex = 0;
  144.     while (count--) {
  145.         *pCellArray++ = cellIndex;
  146.         cellIndex += 3;
  147.     };
  148.     
  149.     /* Create and initialize the auxiliary information. */
  150.     
  151.     aux = (auxInfo**)NewHandle(500);
  152.     (**theList).userHandle = (Handle)aux;
  153.     (**aux).cachedPictID = 0;
  154.     SetResLoad(false);
  155.     for (i=0; true; i++) {
  156.         theStrings = GetResource('STR#', firstID + i);
  157.         if (!theStrings) break;
  158.         (**aux).auxArray[i] = theStrings;
  159.     };
  160.     SetResLoad(true);
  161.     (**aux).auxArray[i] = 0;
  162.     SetHandleSize((Handle)aux, 4*i + sizeof(auxInfo));
  163.     
  164.     /* Invalidate the list rectangle. */
  165.     
  166.     frameRect = (**theList).rView;
  167.     frameRect.right += 15;
  168.     InvalRect(&frameRect);
  169. }
  170.  
  171. /*______________________________________________________________________
  172.  
  173.     rep_Append - Append One Line to a Report.
  174.     
  175.     Entry:    repHandle = handle to report record.
  176.                 repLine = ptr to pascal string to append.
  177.                 drawIt = true to draw the new string, false to not draw it.
  178.                 scroll = true to scroll the report to make the new line
  179.                     visible.
  180.     
  181.     The new line is appended to the end of the report.
  182.     
  183.     If you want the report lines to become visible immediately, specify
  184.     drawIt=true and scroll=true.  The result is similar to the behavior of
  185.     a dumb glass teletype, with the exception that the user can scroll 
  186.     back to previous screens.
  187.     
  188.     If you want to build the report invisibly and then display it,
  189.     specify drawIt=false and scroll=false.  Then, when the report is 
  190.     complete, simply invalidate the rectangle.  This will generate an 
  191.     update event, and rep_Update will display the report.
  192.     
  193.     Don't forget to do a SetPort to the window containing the report
  194.     before calling this routine.
  195.     
  196.     The routine simply returns for type 1 reports.
  197. _____________________________________________________________________*/
  198.  
  199.  
  200. void rep_Append (Handle repHandle, Str255 *repLine, 
  201.     Boolean drawIt, Boolean scroll)
  202.  
  203. {
  204.     ListHandle    theList;            /* handle to list record */
  205.     Cell            theCell;            /* new cell to be added */
  206.  
  207.     theList = (ListHandle) repHandle;
  208.     if ((**theList).refCon) return;
  209.     SetPt(&theCell, 0, (**theList).dataBounds.bottom);
  210.     LDoDraw(drawIt, theList);
  211.     (void) LAddRow(1, theCell.v, theList);
  212.     if (scroll) LScroll(0, theCell.v, theList);
  213.     LSetCell((Ptr)repLine+1, (short)(*repLine)[0], theCell, theList);
  214. }
  215.  
  216. /*______________________________________________________________________
  217.  
  218.     rep_Fill - Append Lines to a Report from an STR# resource.
  219.     
  220.     Entry:    repHandle = handle to report record.
  221.                 id = resource id of STR# resource.
  222.                 scroll = true if report should be scrolled to show last line.
  223.  
  224.     The new lines are appended to the end of the report.
  225.  
  226.     Don't forget to do a SetPort to the window containing the report
  227.     before calling this routine.
  228.     
  229.     The routine simply returns for type 1 reports.
  230. _____________________________________________________________________*/
  231.  
  232. void rep_Fill (Handle repHandle, short id, 
  233.     Boolean scroll)
  234.  
  235. {
  236.     Handle        theStrings;        /* handle to STR# resource */
  237.     ListHandle    theList;            /* handle to list record */
  238.     unsigned char    *p;                /* pointer to current loc in STR# resource */
  239.     short            count;            /* number of lines left to append */
  240.     Point            theCell;            /* new cell to be added */
  241.     Rect            frameRect;        /* rectangle enclosing the list */
  242.  
  243.     theList = (ListHandle) repHandle;
  244.     if ((**theList).refCon) return;
  245.     SetPt(&theCell, 0, (**theList).dataBounds.bottom);
  246.     theStrings = GetResource('STR#', id);
  247.     MoveHHi(theStrings);
  248.     HLock(theStrings);
  249.     p = (unsigned char *)*theStrings;
  250.     count = *((short*) p);
  251.     p += 2;
  252.     LDoDraw(false, theList);
  253.     (void) LAddRow(count, theCell.v, theList);
  254.     while (count-- > 0) {
  255.         LSetCell((Ptr)p+1, (short)*p, theCell, theList);
  256.         p += *p + 1;
  257.         theCell.v++;
  258.     };
  259.     HUnlock(theStrings);
  260.     if (scroll) LScroll(0, theCell.v, theList);
  261.     LDoDraw(true, theList);
  262.     frameRect = (**theList).rView;
  263.     frameRect.right += 15;
  264.     InvalRect(&frameRect);
  265. };
  266.  
  267. /*______________________________________________________________________
  268.  
  269.     rep_Scroll - Process a Mouse Hit in a Scroll Bar.
  270.     
  271.     Entry:    repHandle = handle to report record.
  272.                 where    = pointer to mouse click location, in local coords.
  273.     
  274.     Call this routine to respond to a mouse hit in the scroll bar.
  275.     For convenience, you can call it for a mouse hit anywhere in the entire 
  276.     report rectangle.  The routine checks to make sure the hit is
  277.     really in the actual scroll bar, and returns if it isn't.
  278.     
  279.     Note that mouse down events in the report proper are ignored by this
  280.     routine.  This is the routine you should call if you do not wish to
  281.     allow users to select report lines.
  282.  
  283.     Don't forget to do a SetPort to the window containing the report
  284.     before calling this routine.
  285. _____________________________________________________________________*/
  286.  
  287.  
  288. void rep_Scroll (Handle repHandle, Point where)
  289.  
  290. {
  291.     ListHandle    theList;            /* handle to list record */
  292.  
  293.     theList = (ListHandle) repHandle;
  294.     if (!PtInRect(where, &(**((**theList).vScroll)).contrlRect)) return;
  295.     LDoDraw(true, theList);
  296.     (void) LClick(where, 0, theList);
  297. }
  298.  
  299. /*______________________________________________________________________
  300.  
  301.     rep_Click - Process a Mouse Down in a Report.
  302.     
  303.     Entry:    repHandle = handle to report record.
  304.                 where    = pointer to mouse click location, in local coords.
  305.                 
  306.     Exit:        function result = selected line number, or -1 if none.
  307.     
  308.     Call this routine to respond to a mouse hit in the report rectangle.
  309.     If the mouse hit is in the scroll bar, the report is scrolled.
  310.     Otherwise, a report line may be selected, in which case the routine
  311.     immediately deselects the line and returns its line number.
  312.     
  313.     Note that mouse down events in the report proper are not ignored by
  314.     this routine.  This is the routine you should call if you wish to
  315.     allow users to select report lines.
  316.     
  317.     This routine can be used only with type 0 reports.  It simply returns
  318.     -1 for type 1 reports.
  319.  
  320.     Don't forget to do a SetPort to the window containing the report
  321.     before calling this routine.
  322. _____________________________________________________________________*/
  323.  
  324.  
  325. short rep_Click (Handle repHandle, Point where)
  326.  
  327. {
  328.     ListHandle    theList;            /* handle to list record */
  329.     Cell            theCell;            /* selected cell */
  330.  
  331.     theList = (ListHandle) repHandle;
  332.     if ((**theList).refCon) return -1;
  333.     LDoDraw(true, theList);
  334.     (void) LClick(where, 0, theList);
  335.     theCell.h = theCell.v = 0;
  336.     if (LGetSelect(true, &theCell, theList)) {
  337.         LSetSelect(false, theCell, theList);
  338.         return theCell.v;
  339.     } else {
  340.         return -1;
  341.     };
  342. }
  343.  
  344. /*______________________________________________________________________
  345.  
  346.     rep_Update - Process an Update Event.
  347.     
  348.     Entry:    repHandle = handle to report record.
  349.     
  350.     This routine should be called whenever an update event occurs for
  351.     the window containing the report.  The report, scroll bar, and a 
  352.     rectangle framing the whole thing are drawn.  The report is drawn 
  353.     using the current window font.
  354.  
  355.     Don't forget to do a SetPort to the window containing the report
  356.     before calling this routine, and call BeginUpdate.
  357. _____________________________________________________________________*/
  358.  
  359.  
  360. void rep_Update (Handle repHandle)
  361.  
  362. {
  363.     ListHandle    theList;            /* handle to list record */
  364.     Rect            frameRect;        /* rectangle to be framed */
  365.  
  366.     theList = (ListHandle) repHandle;
  367.     
  368.     /* Draw the framing rectangle. */
  369.     
  370.     frameRect = (**theList).rView;
  371.     frameRect.right += 15;
  372.     InsetRect(&frameRect, -1, -1);
  373.     FrameRect(&frameRect);
  374.     
  375.     /* Call LUpdate to redraw the list and the scroll bar. */
  376.     
  377.     LDoDraw(true, theList);
  378.     LUpdate((**theList).port->visRgn, theList);
  379.     
  380.     /* The following FrameRect is a cludge for when an update event occurs
  381.         when the scroll bar is inactive (e.g., if a screen saver deactivates).
  382.         In this case the LUpdate call doesn't redraw the enclosing rectangle of 
  383.         the scroll bar, because it thinks it's invisible.  So we redraw it 
  384.         here. */
  385.     
  386.     FrameRect(&(**((**theList).vScroll)).contrlRect);
  387. }
  388.  
  389. /*______________________________________________________________________
  390.  
  391.     rep_Activate - Process an Activate or Deactivate Event.
  392.     
  393.     Entry:    repHandle = handle to report record.
  394.                 activate = true to activate, false to deactivate.
  395.                 
  396.     This routine should be called whenever an activate or deactivate
  397.     event occurs.  All it does is hilite or unhilite the scroll
  398.     bar.  You should also call it to temporarily disable the scroll
  399.     bar, e.g., during generation of a report, when you aren't checking
  400.     for mouse hits anyway.  This avoids the ugly "bouncing thumb"
  401.     effect.
  402.  
  403.     Don't forget to do a SetPort to the window containing the report
  404.     before calling this routine.
  405. _____________________________________________________________________*/
  406.  
  407.  
  408. void rep_Activate (Handle repHandle, Boolean active)
  409.  
  410. {
  411.     ListHandle    theList;            /* handle to list record */
  412.     Rect            scrollRect;        /* scroll bar rectangle */
  413.  
  414.     theList = (ListHandle) repHandle;
  415.     LDoDraw(true, theList);
  416.     LActivate(active, theList);
  417.     
  418.     /* The following code fixes what look like bugs in the LActivate
  419.         routine.  
  420.         
  421.         On an activate event LActivate doesn't properly hilite
  422.         the scroll bar.  We invalidate the scroll bar in this case, to
  423.         force an update event.  This causes rep_Update to be called, which 
  424.         in turn calls LUpdate, which redraws it properly hilited.
  425.         
  426.         On a deactivate event LActivate erases the entire scroll bar,
  427.         including its enclosing rectangle.  We want just the interior
  428.         erased, so we have to do a FrameRect.  Also, for some reason
  429.         LActivate invalidates the scroll bar in this case, which generates
  430.         an update event.  This produces an ugly blip when the scroll bar is 
  431.         erased and redrawn by LUpdate in the subsequent call to rep_Update.  
  432.         So we validate the scroll bar rectangle to prevent the update event.
  433.         
  434.         Apparently LActivate is invalidating the scroll bar on deactivate
  435.         events, but not on activate events.  It seems to me that it should
  436.         be exactly the other way around.  But what do I know.
  437.         
  438.         I don't pretend to really understand what's going on here.  But
  439.         my cludges here and in rep_Update seem to fix the problems. */
  440.         
  441.     scrollRect = (**((**theList).vScroll)).contrlRect;
  442.     if (active){
  443.         InvalRect(&scrollRect);
  444.     } else {
  445.         FrameRect(&scrollRect);
  446.         ValidRect(&scrollRect);
  447.     };
  448. }
  449.  
  450. /*______________________________________________________________________
  451.  
  452.     Write0 - Write the Report File for a Type 0 Report.
  453.     
  454.     Entry:    theList = handle to list record.
  455.                 refNum = file reference number.
  456.                 
  457.     Exit:        function result = result code.
  458. _____________________________________________________________________*/
  459.  
  460.  
  461. static OSErr Write0 (ListHandle theList, short refNum)
  462.  
  463. {
  464.     OSErr                rCode;            /* result code */
  465.     Handle            theCells;        /* handle to the list cell data */
  466.     unsigned char    *buf;                /* ptr to beginning of output buffer */
  467.     unsigned char    *pTo;                /* current position in output buffer */
  468.     unsigned char    *pFrom;            /* current position in input buffer */
  469.     short                *cellArray;        /* current position in cell array */
  470.     short                nLines;            /* number of lines left to write */
  471.     short                nBytes;            /* number of bytes to copy */
  472.     long                count;            /* number of bytes to write to file */
  473.         
  474.     /* Get a buffer big enough to hold the entire file. */
  475.     
  476.     theCells = (Handle)(**theList).cells;
  477.     nLines = (**theList).dataBounds.bottom;
  478.     pTo = buf = (unsigned char *)NewPtr(GetHandleSize(theCells) + nLines);
  479.         
  480.     /* Copy cell data from the list to the buffer, adding a return
  481.         character at the end of each line. */
  482.         
  483.     MoveHHi(theCells);
  484.     HLock(theCells);
  485.     pFrom = (unsigned char *)*theCells;
  486.     cellArray = (**theList).cellArray;
  487.     while (nLines--) {
  488.         nBytes = *(cellArray+1) - *cellArray;
  489.         memcpy(pTo, pFrom, nBytes);
  490.         pFrom += nBytes;
  491.         pTo += nBytes;
  492.         *pTo++ = '\r';
  493.         cellArray++;
  494.     };
  495.     HUnlock(theCells);
  496.     
  497.     /* Write the buffer to the file and dispose of the buffer. */
  498.     
  499.     count = pTo-buf;
  500.     rCode = FSWrite(refNum, &count, buf);
  501.     DisposPtr(buf);
  502.     return rCode;
  503. }
  504.  
  505. /*______________________________________________________________________
  506.  
  507.     Write1 - Write the Report File for a Type 1 Report.
  508.     
  509.     Entry:    theList = handle to list record.
  510.                 refNum = file reference number.
  511.                 
  512.     Exit:        function result = result code.
  513. _____________________________________________________________________*/
  514.  
  515.  
  516. static OSErr Write1 (ListHandle theList, short refNum)
  517.  
  518. {
  519.     OSErr                rCode;            /* result code */
  520.     auxInfo            **aux;            /* handle to auxiliary info */
  521.     short                i;                    /* index in STR# handle array */
  522.     Handle            theStrings;        /* handle to STR# resource */    
  523.     unsigned char    *buf;                /* ptr to beginning of output buffer */
  524.     unsigned char    *pTo;                /* current position in output buffer */
  525.     unsigned char    *p;                /* current position in input buffer */
  526.     unsigned char    *q;                /* current position in input line */
  527.     unsigned char    *qEnd;            /* pointer to end of input line */
  528.     short                nLines;            /* number of lines left to write */
  529.     short                nBytes;            /* number of bytes to copy */
  530.     long                count;            /* number of bytes to write to file */
  531.     Boolean            skip;                /* true if line should be skipped */
  532.     
  533.     /* Get handle to auxiliary info. */
  534.     
  535.     aux = (auxInfo**)(**theList).userHandle;
  536.     
  537.     /* The auxiliary array contains handles to the STR# resources.  The
  538.         main loop walks this array and processes one resource at a time. */
  539.     
  540.     i = 0;
  541.     while (true) {
  542.         if (!(theStrings = (**aux).auxArray[i])) break;
  543.         if (!*theStrings) LoadResource(theStrings);
  544.         MoveHHi(theStrings);
  545.         HLock(theStrings);
  546.         
  547.         /* Allocate a buffer big enough to write this resource. */
  548.  
  549.         p = (unsigned char *)*theStrings;
  550.         nLines = *(short*)p;
  551.         p += 2;
  552.         pTo = buf = (unsigned char *)NewPtr(GetHandleSize(theStrings) - 2 + nLines);
  553.         
  554.         /* Copy each line from the STR# resource to the buffer, adding
  555.             a return character at the end of each line.    Don't copy lines which 
  556.             contain an "only" escape sequence that specifies that this line is not
  557.             for the saved version.  Don't copy lines which contain a "pict"
  558.             escape sequence.  Discard other escape sequences.  Don't copy
  559.             end-of-paragraph marks. */
  560.         
  561.         while (nLines--) {
  562.             nBytes = *p;
  563.             q = p+1;
  564.             qEnd = q + nBytes;
  565.             skip = false;
  566.             while (q < qEnd && *q < 31) {
  567.                 if (*q == docPict ||
  568.                     (*q == docOnly && !(*(q+2) & docSave))) {
  569.                     skip = true;
  570.                     break;
  571.                 };
  572.                 nBytes -= *(q+1);
  573.                 q += *(q+1);
  574.             };
  575.             if (!skip) {
  576.                 if (nBytes && *(q+nBytes-1) == docEop) nBytes--;
  577.                 memcpy(pTo, q, nBytes);
  578.                 pTo += nBytes;
  579.                 *pTo++ = '\r';
  580.             };
  581.             p += *p + 1;
  582.         };
  583.         HUnlock(theStrings);
  584.         
  585.         /* Write the buffer to the file and dispose of the buffer. */
  586.         
  587.         count = pTo - buf;
  588.         rCode = FSWrite(refNum, &count, buf);
  589.         DisposPtr(buf);
  590.         if (rCode) return rCode;
  591.         
  592.         i++;
  593.     };
  594.     
  595.     return noErr;
  596. }
  597.  
  598. /*______________________________________________________________________
  599.  
  600.     Write - Write the Report File.
  601.     
  602.     Entry:    repHandle = handle to report record.
  603.                 creator = creator type for the file.
  604.                 fName = pointer to file name.
  605.                 vRefNum = volume reference number.
  606.                 
  607.     Exit:        function result = result code.
  608. _____________________________________________________________________*/
  609.  
  610.  
  611. static OSErr Write (Handle repHandle, OSType creator, 
  612.     Str255 *fName, short vRefNum)
  613.  
  614. {
  615.     ListHandle        theList;            /* handle to list record */
  616.     OSErr                rCode;            /* result code */
  617.     short                refNum;            /* file reference number */
  618.     
  619.     /* Create the file.  Delete it if it already exists. */
  620.     
  621.     if ((rCode = Create((StringPtr)fName, vRefNum, creator, (OSType)'TEXT')) == dupFNErr) {
  622.         if (rCode = FSDelete((StringPtr)fName, vRefNum)) return rCode;
  623.         rCode = Create((StringPtr)fName, vRefNum, creator, (OSType)'TEXT');
  624.     };
  625.     if (rCode) return rCode;
  626.     
  627.     /* Open the file. */
  628.     
  629.     if (rCode = FSOpen((StringPtr)fName, vRefNum, &refNum)) return rCode;
  630.     
  631.     /* Write the file. */
  632.     
  633.     theList = (ListHandle) repHandle;
  634.     if ((**theList).refCon) {
  635.         rCode = Write1 (theList, refNum); 
  636.     } else {
  637.         rCode = Write0 (theList, refNum);
  638.     };
  639.     if (rCode) {
  640.         (void) FSClose(refNum);
  641.         return rCode;
  642.     };
  643.     
  644.     /* Close the file. */
  645.     
  646.     if (rCode = FSClose(refNum)) return rCode;
  647.     return noErr;
  648. }
  649.  
  650. /*______________________________________________________________________
  651.  
  652.     rep_Save - Save a Report as a Text File.
  653.     
  654.     Entry:    repHandle = handle to report record.
  655.                 prompt = pointer to SFPutFile prompt sting.
  656.                 defName = pointer to default file name.
  657.                 creator = creator type for the file.
  658.                 menuPick = true if save operation was initiated via a 
  659.                     menu pick, false if it was initiated via a command key.
  660.                 
  661.     Exit:        good = true if report saved, false if canceled by user.
  662.                 function result = result code.
  663. _____________________________________________________________________*/
  664.  
  665.  
  666. OSErr rep_Save (Handle repHandle, Str255 *prompt, 
  667.     Str255 *defName, OSType creator, Boolean *good, Boolean menuPick)
  668.  
  669. {
  670.     Point                where;            /* location of SFPutFile dialog */
  671.     SFReply            reply;            /* SFPutFile reply */
  672.     Handle            dlgHandle;        /* handle to SFPutFile DLOG resource */
  673.     Rect                dlgRect;            /* SFPutFile dialog rectangle */
  674.     OSErr                rCode;            /* result code */
  675.     CursHandle        watch;            /* handle to watch cursor */
  676.     
  677.     /* Center the SFPutFile dialog. */
  678.     
  679.     dlgHandle = GetResource('DLOG', putDlgID);
  680.     dlgRect = **(Rect**)dlgHandle;
  681.     utl_CenterDlogRect(&dlgRect, menuPick);
  682.     SetPt(&where, dlgRect.left, dlgRect.top);
  683.     
  684.     /* Call SFPutFile.  Return if dialog canceled. */
  685.     
  686.     utl_FixStdFile();
  687.     SFPutFile(where, (StringPtr)prompt, (StringPtr)defName, nil, &reply);
  688.     if (!(*good = reply.good)) return noErr;
  689.     
  690.     /* Create and write the file. */
  691.     
  692.     watch = GetCursor(watchCursor);
  693.     SetCursor(*watch);
  694.     rCode = Write(repHandle, creator, (Str255 *)reply.fName, reply.vRefNum);
  695.     InitCursor();
  696.     if (rCode) {
  697.         (void) FSDelete(reply.fName, reply.vRefNum);
  698.         return rCode;
  699.     };
  700.     return FlushVol(nil, reply.vRefNum);
  701. }
  702.  
  703. /*______________________________________________________________________
  704.  
  705.     rep_Key - Process Up and Down Arrow Keys.
  706.     
  707.     Entry:    repHandle = handle to report record.
  708.                 key = key pressed (up or down arrow).
  709.                 modifiers = modifier keys.
  710.                 
  711.     Exit:        function result = error code.
  712.  
  713.     Don't forget to do a SetPort to the window containing the report
  714.     before calling this routine.
  715.     
  716.     If the key down event is not an up or down arrow key then the routine
  717.     returns without doing anything.
  718. _____________________________________________________________________*/
  719.  
  720.  
  721. OSErr rep_Key (Handle repHandle, short key, short modifiers)
  722.  
  723. {
  724.  
  725.     ListHandle        theList;                /* handle to list record */
  726.     short                direc;                /* +1 or -1 for direction to scroll */
  727.     short                height;                /* height of report in lines */
  728.     
  729.     if (key == upArrow) {
  730.         direc = -1;
  731.     } else if (key == downArrow) {
  732.         direc = 1;
  733.     } else {
  734.         return noErr;
  735.     };
  736.     theList = (ListHandle) repHandle;
  737.     LDoDraw(true, theList);
  738.     if (modifiers & cmdKey) {
  739.         if (modifiers & shiftKey) {
  740.             LScroll(0, direc*(**theList).dataBounds.bottom, theList);
  741.         } else {
  742.             height = ((**theList).rView.bottom - (**theList).rView.top) /
  743.                 (**theList).cellSize.v - 1;
  744.             LScroll(0, direc*height, theList);
  745.         };
  746.     } else {
  747.         LScroll(0, direc, theList);
  748.     };
  749.     return noErr;
  750. }
  751.  
  752. /*______________________________________________________________________
  753.  
  754.     rep_Jump - Scroll (jump) to a Specified Line.
  755.     
  756.     Entry:    repHandle = handle to report record.
  757.                 lineNum = line number to jump to.
  758.                 doDraw = true to redraw the report rectangle.
  759.  
  760.     Don't forget to do a SetPort to the window containing the report
  761.     before calling this routine.
  762. _____________________________________________________________________*/
  763.  
  764.  
  765. void rep_Jump (Handle repHandle, short lineNum, Boolean doDraw)
  766.  
  767. {
  768.     ListHandle        theList;            /* handle to list record */
  769.     
  770.     theList = (ListHandle) repHandle;
  771.     LDoDraw(doDraw, theList);
  772.     LScroll(0, lineNum - (**theList).visible.top, theList);
  773. }
  774.  
  775. /*______________________________________________________________________
  776.  
  777.     rep_Clear - Clear a Report.
  778.     
  779.     Entry:    repHandle = handle to report record.
  780.  
  781.     Don't forget to do a SetPort to the window containing the report
  782.     before calling this routine.
  783.     
  784.     The routine simply returns for type 1 reports.
  785. _____________________________________________________________________*/
  786.  
  787.  
  788. void rep_Clear (Handle repHandle)
  789.  
  790. {
  791.     ListHandle    theList;            /* handle to list record */
  792.  
  793.     theList = (ListHandle) repHandle;
  794.     if ((**theList).refCon) return;
  795.     LDoDraw(true, theList);
  796.     LDelRow(0, 0, theList);
  797. }
  798.  
  799. /*______________________________________________________________________
  800.  
  801.     rep_Full    - Check a Report to See if it's Full.
  802.     
  803.     Entry:    repHandle = handle to report record.
  804.                 slop = number of bytes to guarantee free.
  805.                 
  806.     Exit:        funtion result = true if not at least slop free bytes.
  807. _____________________________________________________________________*/
  808.  
  809.  
  810. Boolean rep_Full (Handle repHandle, long slop)
  811.  
  812. {
  813.     ListHandle        theList;            /* handle to list record */
  814.     long                repSize;            /* size of list cell data */
  815.  
  816.     theList = (ListHandle) repHandle;
  817.     repSize = GetHandleSize((Handle)(**theList).cells);
  818.     return slop > (0x7fff - repSize);
  819. }
  820.  
  821. /*______________________________________________________________________
  822.  
  823.     rep_Dispose    - Dispose of a report.
  824.     
  825.     Entry:    repHandle = handle to report record.
  826.                 cellOption = CELL resource disposal option (type 1 reports 
  827.                     only):
  828.                     0 = release the CELL resource.
  829.                     1 = keep the CELL resource, but make it purgable.
  830.                     2 = keep the CELL resource, and leave it unpurgable.
  831. _____________________________________________________________________*/
  832.  
  833.  
  834. void rep_Dispose (Handle repHandle, short cellOption)
  835.  
  836. {
  837.     ListHandle            theList;                /* handle to list record */
  838.     auxInfo                **aux;                /* handle to auxiliary info */
  839.  
  840.     theList = (ListHandle) repHandle;
  841.     
  842.     /* For a type 1 report, dispose of the auxiliary info, and process 
  843.         the cell option. */
  844.  
  845.     if ((**theList).refCon) {
  846.         aux = (auxInfo**)(**theList).userHandle;
  847.         if ((**aux).cachedPictID) DisposPtr((**aux).cachedBitMap.baseAddr);
  848.         DisposHandle((Handle)aux);
  849.         switch (cellOption) {
  850.             case 0:
  851.                 ReleaseResource((Handle)(**theList).cells);
  852.                 break;
  853.             case 1:
  854.                 HPurge((Handle)(**theList).cells);
  855.                 break;
  856.             case 2:
  857.                 break;
  858.         };
  859.         (**theList).cells = (DataHandle)NewHandle(0);
  860.     };
  861.     
  862.     /* Dispose of the list. */
  863.     
  864.     LDispose(theList);
  865. }
  866.  
  867. /*______________________________________________________________________
  868.  
  869.     rep_GetSize - Get the Size of a Report.
  870.     
  871.     Entry:    repHandle = handle to report record.
  872.     
  873.     Exit:        function result = number of lines in report.
  874. _____________________________________________________________________*/
  875.  
  876.  
  877. short rep_GetSize (Handle repHandle)
  878.  
  879. {
  880.     ListHandle        theList;                /* handle to list record */
  881.     
  882.     theList = (ListHandle)repHandle;
  883.     return (**theList).dataBounds.bottom;
  884. }
  885.  
  886. /*______________________________________________________________________
  887.  
  888.     rep_Tag - Lookup a Tag.
  889.     
  890.     Entry:    id = TAG resource id.
  891.                 tag = tag to lookup in TAG resource.
  892.     
  893.     Exit:        function result = corresponding line number from
  894.                     tag resource, or 0 if none found.
  895. _____________________________________________________________________*/
  896.  
  897.  
  898. short rep_Tag (short id, short tag)
  899.  
  900. {
  901.     Handle                h;                /* handle to TAG resource */
  902.     char                    *p;            /* pointer into TAG resource */
  903.     short                    n;                /* number of entries in TAG resource */
  904.         
  905.     h = GetResource('TAG ', id);
  906.     if (!h) return 0;
  907.     p = *h;
  908.     n = *(short*)p;
  909.     p += 4;
  910.     while (n--) {
  911.         if (tag == *(short*)p) return *(short*)(p-2);
  912.         p += 4;
  913.     };
  914.     return 0;
  915. }
  916.  
  917. /*______________________________________________________________________
  918.  
  919.     rep_Height - Set New Report Height.
  920.     
  921.     Entry:    repHandle = handle to report record.
  922.                 bottom = new bottom coord.
  923.                 
  924.     The new height is rounded down to the nearest multiple of the cell
  925.     height.
  926. _____________________________________________________________________*/
  927.  
  928.  
  929. void rep_Height (Handle repHandle, short bottom)
  930.  
  931. {
  932.     ListHandle        theList;                /* handle to list record */
  933.     short                oldHeight;            /* old height of list rect */
  934.     short                newHeight;            /* new height of list rect */
  935.     Rect                inval;                /* rect to invalidate */
  936.     
  937.     theList = (ListHandle)repHandle;
  938.     oldHeight = (**theList).rView.bottom - (**theList).rView.top;
  939.     newHeight = bottom - (**theList).rView.top;
  940.     newHeight -= newHeight % (**theList).cellSize.v;
  941.     if (oldHeight == newHeight) return;
  942.     LDoDraw(false, theList);
  943.     LSize((**theList).rView.right - (**theList).rView.left,
  944.         newHeight, theList);
  945.     LDoDraw(true, theList);
  946.     inval = (**theList).rView;
  947.     InsetRect(&inval, -1, -1);
  948.     inval.right += 15;
  949.     inval.bottom = (**theList).port->portRect.bottom;
  950.     InvalRect(&inval);
  951. }
  952.  
  953. /*______________________________________________________________________
  954.  
  955.     rep_GetRect - Get Report Rectangle.
  956.     
  957.     Entry:    repHandle = handle to report record.
  958.                 
  959.     Exit:        rect = report rectangle.
  960. _____________________________________________________________________*/
  961.  
  962.  
  963. void rep_GetRect (Handle repHandle, Rect *rect)
  964.  
  965. {
  966.     ListHandle        theList;                /* handle to list record */
  967.     
  968.     theList = (ListHandle)repHandle;
  969.     *rect = (**theList).rView;
  970.     rect->right += 15;
  971. }
  972.  
  973. /*______________________________________________________________________
  974.  
  975.     rep_GetPos - Get Report Scroll Position.
  976.     
  977.     Entry:    repHandle = handle to report record.
  978.                 
  979.     Exit:        function result = line number of top visible line in report.
  980. _____________________________________________________________________*/
  981.  
  982.  
  983. short rep_GetPos (Handle repHandle)
  984.  
  985. {
  986.     ListHandle        theList;                /* handle to list record. */
  987.     
  988.     theList = (ListHandle)repHandle;
  989.     return (**theList).visible.top;
  990. }